package com.cls.business.service.impl;

import cn.hutool.poi.word.Word07Writer;
import cn.hutool.poi.word.WordUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cls.business.entity.*;
import com.cls.business.mapper.DeclarePlanMapper;
import com.cls.business.mapper.EntrustTopicMapper;
import com.cls.business.mapper.TopicMapper;
import com.cls.business.mapper.TopicPlanMapper;
import com.cls.business.service.IDeclareTopicService;
import com.cls.business.service.ITopicService;
import com.cls.business.service.ITopicSubjectDetailService;
import com.cls.business.service.ITopicSubjectService;
import com.cls.common.entity.QueryRequest;
import com.cls.common.enums.DictEnum;
import com.cls.system.entity.Configure;
import com.cls.system.mapper.ConfigureMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 课题 Service实现
 *
 * @author weimaomao
 * @date 2020-11-10 09:57:54
 */
@Service
@RequiredArgsConstructor
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class TopicServiceImpl extends ServiceImpl<TopicMapper, Topic> implements ITopicService {

    private final TopicPlanMapper topicPlanMapper;

    private final EntrustTopicMapper entrustTopicMapper;

    private final IDeclareTopicService declareTopicService;

    private final ITopicSubjectDetailService topicSubjectDetailService;

    private final ITopicSubjectService topicSubjectService;

    private final DeclarePlanMapper declarePlanMapper;

    private final ConfigureMapper configureMapper;


    private List<Topic> findTopicsByYear(String year) {
        LambdaQueryWrapper<Topic> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Topic::getYear, year);
        queryWrapper.orderByAsc(Topic::getTopicClassifyCode);
        queryWrapper.orderByAsc(Topic::getTopicLevelType);
        queryWrapper.orderByAsc(Topic::getCreateTime);
        List<Topic> list = this.list(queryWrapper);
        return list;
    }

    @Override
    public PageInfo<Topic> findTopics(QueryRequest request, Topic topic) {
        LambdaQueryWrapper<Topic> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Topic::getYear, topic.getYear());
        if (StringUtils.isNotEmpty(topic.getTopicName())) {
            queryWrapper.like(Topic::getTopicName, topic.getTopicName());
        }
        queryWrapper.apply(StringUtils.isNotEmpty(topic.getSubjectTypeCode()), "FIND_IN_SET ('" + topic.getSubjectTypeCode() + "',subject_type_codes)");
        queryWrapper.orderByAsc(Topic::getCodeOrder);
        queryWrapper.orderByAsc(Topic::getCreateTime);
        // TODO 设置查询条件
        PageHelper.startPage(request.getPageNum(), request.getPageSize());
        List<Topic> list = this.list(queryWrapper);
        PageInfo pageInfo = new PageInfo(list);
        return pageInfo;
    }

    @Override
    public List<Topic> getPublishedTopics(Topic topic) {
        LambdaQueryWrapper<Topic> queryWrapper = new LambdaQueryWrapper<>();
        if (topic.getYear() != null) {
            queryWrapper.eq(Topic::getYear, topic.getYear());
        }
        if (topic.getPlanId() != null) {
            queryWrapper.eq(Topic::getPlanId, topic.getPlanId());
        }

        if (topic.getStatusTypeCode() != null) {
            queryWrapper.eq(Topic::getStatusTypeCode, topic.getStatusTypeCode());
        }

        if (StringUtils.isNotBlank(topic.getTopicClassifyCode())) {
//            queryWrapper.eq(Topic::getTopicClassifyCode, topic.getTopicClassifyCode());
            queryWrapper.and(wrapper -> {
                wrapper.eq(Topic::getTopicClassifyCode, topic.getTopicClassifyCode()).or()
                        .eq(Topic::getTopicClassifyCode, DictEnum.UNLIMITED.getCode());
            });
        }
        queryWrapper.orderByAsc(Topic::getCodeOrder);
        queryWrapper.orderByAsc(Topic::getCreateTime);
        queryWrapper.isNotNull(Topic::getTopicCode);
        List<Topic> topics = this.baseMapper.selectList(queryWrapper);
        return topics;
    }

    @Override
    public List<Topic> findTopicsByPlanId(Long planId) {
        // TODO 设置查询条件
        return this.baseMapper.selectListByPlanId(planId);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createTopic(Topic topic) {
        //设置计划的年份
        topic.setDeclareNum(0);
        topic.setQualifiedPassNum(0);
        topic.setProjectNum(0);
        topic.setStatusTypeCode(DictEnum.PLAN_CREATE.getCode());
        TopicPlan byId = topicPlanMapper.selectById(topic.getPlanId());
        topic.setYear(byId.getYear());
        //委托课题不创建计划
        if (DictEnum.ENTRUST.getCode().equals(topic.getTopicClassifyCode())) {
            topic.setPlanId(null);
        }
        this.save(topic);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTopic(Topic topic) {
        //委托课题不创建计划
        if (DictEnum.ENTRUST.getCode().equals(topic.getTopicClassifyCode())) {
            topic.setPlanId(null);
        }
        this.saveOrUpdate(topic);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteTopic(Topic topic) {
        LambdaQueryWrapper<Topic> wrapper = new LambdaQueryWrapper<>();
        // TODO 设置删除条件
        this.remove(wrapper);
    }

    /**
     * 删除
     *
     * @param ids
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteTopics(String[] ids) {
        List<String> list = Arrays.asList(ids);
        this.removeByIds(list);
    }

    @Override
    public void createTopicCode(Long planId, String year) {
        //根据计划id查询已定级的课题
        List<Topic> topicList = this.findTopicsByYear(year);
        AtomicInteger number = new AtomicInteger(1);
        topicList.forEach(item -> {
            int order = number.getAndIncrement();
            item.setTopicCode(year + "-" + order);
            item.setCodeOrder(order);
            this.updateById(item);
        });
        TopicPlan topicPlan = new TopicPlan();
        topicPlan.setPlanId(planId);
        topicPlan.setIsGenerateCode(Boolean.TRUE);
        topicPlanMapper.updateById(topicPlan);
    }

    @Override
    public void download(Long planId, HttpServletResponse response) {
        TopicPlan topicPlan = topicPlanMapper.selectById(planId);
        List<Topic> topics = this.findTopicsByYear(topicPlan.getYear());
        Word07Writer writer = WordUtil.getWriter();
        writer.addText(ParagraphAlignment.CENTER, new Font("宋体", Font.BOLD, 18), "中国法学会" + topicPlan.getYear() + "年度部级法学研究课题指南");
        //换个行
        writer.addText(new Font("宋体", Font.PLAIN, 8), "");
        topics.forEach(item -> {
            writer.addText(new Font("宋体", Font.PLAIN, 16), "  ", item.getTopicCode(), ". ", item.getTopicName());
        });
        OutputStream out = null;
        try {
            out = response.getOutputStream();
            writer.flush(out);
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean autoWriteSubjectType(List<Topic> topics, List<DeclareTopic> declareTopics, List<TopicSubjectDetail> details) {
        if (topics != null) {
            topics.forEach(item->{
                saveOrUpdate(item);
            });
        }

        if (declareTopics != null) {
            declareTopics.forEach(item->{
                declareTopicService.saveOrUpdate(item);
            });
        }

        if (details != null) {
            details.forEach(item->{
                topicSubjectDetailService.saveOrUpdate(item);
            });
        }

        return true;
    }

    @Override
    public PageInfo<Topic> findReviewTopics(QueryRequest request, Topic topic) {
        LambdaQueryWrapper<Topic> queryWrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotEmpty(topic.getTopicName())) {
            queryWrapper.like(Topic::getTopicName, topic.getTopicName());
        }

        if (StringUtils.isNotEmpty(topic.getTopicClassifyCode())) {
            // 添加不限
            queryWrapper.and(wrapper -> {
                wrapper.eq(Topic::getTopicClassifyCode, topic.getTopicClassifyCode()).or()
                        .eq(Topic::getTopicClassifyCode, DictEnum.UNLIMITED.getCode());
            });
        }

        if (topic.getYear() != null) {
            queryWrapper.like(Topic::getYear, topic.getYear());
        }

        if (StringUtils.isNotEmpty(topic.getStatusTypeCode())) {
            queryWrapper.like(Topic::getStatusTypeCode, topic.getStatusTypeCode());
        }

        // TODO 设置查询条件
        PageHelper.startPage(request.getPageNum(), request.getPageSize());
        List<Topic> list = list(queryWrapper);
        for(Topic one:list){
            TopicPlan plan = topicPlanMapper.selectById(one.getPlanId());
            one.setPlanStatusCode(plan.getStatusCode());
        }
        PageInfo pageInfo = new PageInfo(list);
        return pageInfo;
    }

    @Override
    public List<Topic> findReviewTopics(Topic topic) {
        LambdaQueryWrapper<Topic> queryWrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotEmpty(topic.getTopicName())) {
            queryWrapper.like(Topic::getTopicName, topic.getTopicName());
        }

        if (StringUtils.isNotEmpty(topic.getTopicClassifyCode())) {
            queryWrapper.eq(Topic::getTopicClassifyCode, topic.getTopicClassifyCode());
        }

        if (topic.getYear() != null) {
            queryWrapper.like(Topic::getYear, topic.getYear());
        }

        return list(queryWrapper);
    }

    @Override
    @Transactional
    public boolean classifyTopic(Long planId, Long topicId, List<TopicSubjectDetail> details) {
        // 更新指南中的课题分类标识
        Topic topic = new Topic();
        topic.setTopicId(topicId);
        topic.setStatusTypeCode(DictEnum.PLAN_SUBJECT.getCode());
        if(details != null && details.size() > 0){
            // 生成学科分类
            List<String> subjectTypes = createTopicSubject(planId, topicId, details);
            // 选题中写入学科分类
            topic.setSubjectTypeCodes(subjectTypes);
        }
        baseMapper.updateById(topic);
        return true;
    }

    private List<String> createTopicSubject(Long planId, Long topicId, List<TopicSubjectDetail> details) {
        // 获取申报课题最多的学科，并生成学科列表
        List<TopicSubject> subjects = new ArrayList();
        String maxSubjectType = "";
        List<String> subjectTypes = new ArrayList();
        int maxNum = -1;
        for (TopicSubjectDetail detail : details) {
            // 保存已选择的分类，置上选中标识
            subjectTypes.add(detail.getSubjectTypeCode());
            detail.setChoose(true);
            // 记录种类最多的一类学科，之后给申报课题重新分配学科类型
            int num = detail.getNum();
            if (num > maxNum) {
                maxNum = num;
                maxSubjectType = detail.getSubjectTypeCode();
            }
        }

        // 修改申报课题中的学科分类，并统计个数
        List<DeclareTopic> declareTopics = declarePlanMapper.findDeclareTopicByPlanIdAndTopicId(planId, topicId);
        int modifyNum = 0;
        for (DeclareTopic one : declareTopics) {
            if (!subjectTypes.contains(one.getSubjectTypeCode())) {
                one.setCheckSubjectTypeCode(maxSubjectType);
                modifyNum++;
            } else {
                one.setCheckSubjectTypeCode(one.getSubjectTypeCode());
            }
            declareTopicService.updateById(one);
        }

        Map<String, Integer> expertTypes = calcExpertTypes(details);

        // 先删除已存在的学科分类，之后再重新插入学科分类并重新计算各个学科分类的个数
        TopicSubject delParam = new TopicSubject();
        delParam.setTopicId(topicId);
        delParam.setPlanId(planId);
        topicSubjectService.deleteTopicSubject(delParam);
        for (TopicSubjectDetail detail : details) {
            TopicSubject topicSubject = new TopicSubject();
            topicSubject.setTopicId(detail.getTopicId());
            topicSubject.setSubjectTypeCode(detail.getSubjectTypeCode());
            topicSubject.setPlanId(detail.getPlanId());
            topicSubject.setNum(detail.getNum());
            if (maxSubjectType.equals(detail.getSubjectTypeCode())) {
                int beforeNum = detail.getNum();
                topicSubject.setNum(beforeNum + modifyNum);
            }
            topicSubject.setSubjectNum(expertTypes.get(detail.getSubjectTypeCode()));
            subjects.add(topicSubject);
        }
        subjects.forEach(item->{
            topicSubjectService.save(item);
        });

        // 更新手动分类标识
        topicSubjectDetailService.clearChooseByTopicId(topicId);
        details.forEach(item->{
            topicSubjectDetailService.saveOrUpdate(item);
        });
        return subjectTypes;
    }

    private Map<String, Integer> calcExpertTypes(List<TopicSubjectDetail> details){
        if(details == null || details.size() < 1){
            return null;
        }
        List<Configure> configures = configureMapper.selectByMap(null);
        int expertNum = 3;
        if(configures != null && configures.size() > 1){
            expertNum = configures.get(0).getExpertNum();
        }
        Map<String, Integer> expertTypes = new HashMap();
        List<String> randomTypes = new ArrayList();
        for(TopicSubjectDetail one: details) {
            expertTypes.put(one.getSubjectTypeCode(), 1);
            for(int i = 0; i < one.getNum(); i++){
                randomTypes.add(one.getSubjectTypeCode());
            }
        }
        int remaining = expertNum - details.size();
        if(remaining > 0){
            for(int i = 0; i < remaining; i++ ){
                Random random = new Random();
                int n = random.nextInt(randomTypes.size());
                String newType = randomTypes.get(n);
                Integer count = expertTypes.get(newType);
                if(count != null){
                    expertTypes.put(newType, count + 1);
                }
            }
        }
        return expertTypes;
    }


    @Override
    @Transactional
    public boolean classifyEntrustTopic(Long planId, Long topicId, List<TopicSubjectDetail> details) {
        // 更新指南中的课题分类标识
        EntrustTopic topic = new EntrustTopic();
        topic.setTopicId(topicId);
        topic.setStatusTypeCode(DictEnum.PLAN_SUBJECT.getCode());
        if(details != null && details.size() > 0) {
            List<String> subjectTypes = createTopicSubject(planId, topicId, details);
            topic.setSubjectTypeCodes(subjectTypes);
        }
        entrustTopicMapper.updateById(topic);
        return true;
    }


}
